package cn.icanci.loopstack.utils.time;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;

/**
 * 时间格式化工具类，线程安全工具类，基于Java8新时间API
 * 
 * @author icanci
 * @since 1.0 Created in 2022/12/24 20:45
 */
@SuppressWarnings("all")
public class DateTimeUtils {
    private DateTimeUtils() {
    }

    // format pattern
    public static final String                          DEFAULT_PURE_DATETIME       = "1900-01-01 00:00:00";
    public static final String                          DEFAULT_UTC_DATETIME        = "1900-01-01T00:00:00";

    public static final long                            ONE_DAY_SECONDS             = 86400L;
    public static final long                            ONE_DAY_MILL_SECONDS        = 86400000L;

    public static final String                          SHORT_FORMAT                = "yyyyMMdd";
    public static final String                          LONG_FORMAT                 = "yyyyMMddHHmmss";
    public static final String                          LONG_NOS_FORMAT             = "yyyyMMddHHmm";
    public static final String                          WEB_FORMAT                  = "yyyy-MM-dd";
    public static final String                          TIME_FORMAT                 = "HHmmss";
    public static final String                          MONTH_FORMAT                = "yyyyMM";
    public static final String                          CHINESE_DT_FORMAT           = "yyyy年MM月dd日";
    public static final String                          NEW_FORMAT                  = "yyyy-MM-dd HH:mm:ss";
    public static final String                          NO_SECOND_FORMAT            = "yyyy-MM-dd HH:mm";

    // DateTimeFormatters
    private static final DateTimeFormatter              SHORT_FORMAT_FORMATTER      = DateTimeFormatter.ofPattern(SHORT_FORMAT);
    private static final DateTimeFormatter              LONG_FORMAT_FORMATTER       = DateTimeFormatter.ofPattern(LONG_FORMAT);
    private static final DateTimeFormatter              LONG_NOS_FORMAT_FORMATTER   = DateTimeFormatter.ofPattern(LONG_NOS_FORMAT);
    private static final DateTimeFormatter              WEB_FORMAT_FORMATTER        = DateTimeFormatter.ofPattern(WEB_FORMAT);
    private static final DateTimeFormatter              TIME_FORMAT_FORMATTER       = DateTimeFormatter.ofPattern(TIME_FORMAT);
    private static final DateTimeFormatter              MONTH_FORMAT_FORMATTER      = DateTimeFormatter.ofPattern(MONTH_FORMAT);
    private static final DateTimeFormatter              CHINESE_DT_FORMAT_FORMATTER = DateTimeFormatter.ofPattern(CHINESE_DT_FORMAT);
    private static final DateTimeFormatter              NEW_FORMAT_FORMATTER        = DateTimeFormatter.ofPattern(NEW_FORMAT);
    private static final DateTimeFormatter              NO_SECOND_FORMAT_FORMATTER  = DateTimeFormatter.ofPattern(NO_SECOND_FORMAT);

    private static final Map<String, DateTimeFormatter> DATE_TIME_FORMATTER_MAP     = new ConcurrentHashMap<>(16);

    static {
        DATE_TIME_FORMATTER_MAP.put(SHORT_FORMAT, SHORT_FORMAT_FORMATTER);
        DATE_TIME_FORMATTER_MAP.put(LONG_FORMAT, LONG_FORMAT_FORMATTER);
        DATE_TIME_FORMATTER_MAP.put(LONG_NOS_FORMAT, LONG_NOS_FORMAT_FORMATTER);
        DATE_TIME_FORMATTER_MAP.put(WEB_FORMAT, WEB_FORMAT_FORMATTER);
        DATE_TIME_FORMATTER_MAP.put(TIME_FORMAT, TIME_FORMAT_FORMATTER);
        DATE_TIME_FORMATTER_MAP.put(MONTH_FORMAT, MONTH_FORMAT_FORMATTER);
        DATE_TIME_FORMATTER_MAP.put(CHINESE_DT_FORMAT, CHINESE_DT_FORMAT_FORMATTER);
        DATE_TIME_FORMATTER_MAP.put(NEW_FORMAT, NEW_FORMAT_FORMATTER);
        DATE_TIME_FORMATTER_MAP.put(NO_SECOND_FORMAT, NO_SECOND_FORMAT_FORMATTER);
    }

    /**
     * format
     * 
     * @param date date
     * @param format format
     * @return format
     */
    public static String format(Date date, String format) {
        if (StringUtils.isBlank(format) || date == null) {
            return null;
        }

        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);

        if (DATE_TIME_FORMATTER_MAP.containsKey(format)) {
            return DATE_TIME_FORMATTER_MAP.get(format).format(localDateTime);
        }

        try {
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(format);
            DATE_TIME_FORMATTER_MAP.put(format, dateTimeFormatter);
            return dateTimeFormatter.format(localDateTime);
        } catch (UnsupportedTemporalTypeException e) {
            // no op
        }
        return null;
    }

    /**
     * parse
     * 
     * @param dateStr dateStr
     * @param format format
     * @return format
     */
    public static Date parse(String dateStr, String format) {
        if (StringUtils.isBlank(dateStr) || StringUtils.isBlank(format)) {
            return null;
        }

        if (DATE_TIME_FORMATTER_MAP.containsKey(format)) {
            DateTimeFormatter formatter = DATE_TIME_FORMATTER_MAP.get(format);
            return Date.from(LocalDateTime.parse(dateStr, formatter).atZone(ZoneOffset.systemDefault()).toInstant());
        }

        try {
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(format);
            DATE_TIME_FORMATTER_MAP.put(format, dateTimeFormatter);
            return Date.from(LocalDateTime.parse(dateStr, dateTimeFormatter).atZone(ZoneOffset.systemDefault()).toInstant());
        } catch (UnsupportedTemporalTypeException e) {
            // no op
        }

        return null;
    }

    // TODO 暂时用不到这些方法，先不实现
    //    public static final LocalDateTime toLocalDateTime(Date date, String format) {
    //        return null;
    //    }
    //
    //    public static final LocalDateTime toLocalDateTime(String dateStr, String format) {
    //        return null;
    //    }
    //
    //    public static Date addYears(Date date, int years) {
    //        return null;
    //    }
    //
    //    public static Date addMonths(Date date, int months) {
    //        return null;
    //    }
    //
    //    public static Date addWeeks(Date date, int weeks) {
    //        return null;
    //    }
    //
    //    public static Date addDays(Date date, int days) {
    //        return null;
    //    }
    //
    //    public static Date addHours(Date date, int hours) {
    //        return null;
    //    }
    //
    //    public static Date addMinutes(Date date, int minutes) {
    //        return null;
    //    }
    //
    //    public static Date addSeconds(Date date, int seconds) {
    //        return null;
    //    }
    //
    //    public static Date addYears(String date, int years) {
    //        return null;
    //    }
    //
    //    public static Date addMonths(String date, int months) {
    //        return null;
    //    }
    //
    //    public static Date addWeeks(String date, int weeks) {
    //        return null;
    //    }
    //
    //    public static Date addDays(String date, int days) {
    //        return null;
    //    }
    //
    //    public static Date addHours(String date, int hours) {
    //        return null;
    //    }
    //
    //    public static Date addMinutes(String date, int minutes) {
    //        return null;
    //    }
    //
    //    public static Date addSeconds(String date, int seconds) {
    //        return null;
    //    }
    //
    //    public static String addYears(String date, int years, String format) {
    //        return null;
    //    }
    //
    //    public static String addMonths(String date, int months, String format) {
    //        return null;
    //    }
    //
    //    public static String addWeeks(String date, int weeks, String format) {
    //        return null;
    //    }
    //
    //    public static String addDays(String date, int days, String format) {
    //        return null;
    //    }
    //
    //    public static String addHours(String date, int hours, String format) {
    //        return null;
    //    }
    //
    //    public static String addMinutes(String date, int minutes, String format) {
    //        return null;
    //    }
    //
    //    public static String addSeconds(String date, int seconds, String format) {
    //        return null;
    //    }

}
